package org.wattdepot.gdata.kukuicup.updater.energygoalgame;

import java.net.URL;
import java.util.Calendar;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.gdata.kukuicup.service.GDataBatchRequest;
import org.wattdepot.gdata.kukuicup.service.GDataService;
import org.wattdepot.gdata.kukuicup.service.WattDepotService;
import org.wattdepot.gdata.kukuicup.updater.GDataTask;
import org.wattdepot.util.tstamp.Tstamp;

/**
 * Implements the task to obtain the goal and actual consumption for the given source to date 
 * for today.
 * @author Philip Johnson
 */
public class EnergyGoalGameBetaEvalTask extends GDataTask {
  
  /** The goal reduction we will use during this run. */
  private int goalReduction;
  
  /** The warning reduction we will use during this run. */
  private int warningReduction;
  
  /** The data structure that holds the actual consumption values for this lounge. */
  private BetaEvalActualConsumption actuals;
  
  /**
   * Creates the task that gets woken up regularly (typically every 60 minutes). 
   * @param wattDepotService The WattDepot service configuration data. 
   * @param gdataService The gdata service configuration data.
   * @param spreadsheetURL The URL to the spreadsheet containing the data.
   * @param sourceName The WattDepot source of interest.
   * @param isVirtual True if the source is virtual.
   * @param row The row in the spreadsheet that this source's data appears in.
   * @param goalReduction The percentage reduction to meet the goal.
   * @param warningReduction  The percentage reduction to get to green.
   * @param userDataFileName The file containing user data from the web site.
   */
  public EnergyGoalGameBetaEvalTask(WattDepotService wattDepotService, GDataService gdataService, 
      URL spreadsheetURL, String sourceName, boolean isVirtual, int row, int goalReduction,
      int warningReduction, String userDataFileName) {
    super(wattDepotService, gdataService, spreadsheetURL, sourceName, row, "EnergyGoalGameBetaEval",
        isVirtual);
    this.goalReduction = goalReduction;
    this.warningReduction = warningReduction;
    this.actuals = new BetaEvalActualConsumption(sourceName, userDataFileName, gdataService);
  }
  
  /**
   * Return the baseline level of consumption for this source at this moment in time.
   * For the beta test, this is always 5000 Wh per hour.
   * @return The baseline level of consumption.
   */
  public int getBaselineConsumption() {
    Calendar cal = Calendar.getInstance();
    int hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
    int minutes = cal.get(Calendar.MINUTE);
    return ((5000 * hourOfDay) + (5000 * minutes / 60));
  }
  
  /**
   * Return the goal consumption given the baseline.  
   * @param baselineConsumption The baseline consumption.
   * @return The goal consumption.
   */
  public int getGoalConsumption(int baselineConsumption) {
    int goalConsumption = 
      (int) (baselineConsumption - (baselineConsumption * (goalReduction / 100.0))); 
    // Compute and return the goal by subtracting the percentage reduction from the baseline.
    return goalConsumption;
  }
  
  /**
   * Return the warning consumption given the baseline.  
   * @param baselineConsumption The baseline consumption.
   * @return The warning consumption.
   */
  public int getWarningConsumption(int baselineConsumption) {
    int warningConsumption = 
      (int) (baselineConsumption - (baselineConsumption * (warningReduction / 100.0))); 
    // Compute and return the goal by subtracting the percentage reduction from the baseline.
    return warningConsumption;
  }
  
  /**
   * Return the actual consumption from the start of today until now.
   * This is computed using the BetaEvalActualConsumption class.
   * Assumes that this gets called exactly once per hour. 
   * @return The actual consumption to date for today.
   */
  public int getActualConsumption() {
    actuals.updateActualConsumption();
    return actuals.getConsumption(makeDateStamp());
  }

  /**
   * Retrieve the baseline consumption data, calculate the 
   * simulated actual consumption, and write out the current state.
   * Due to BetaEvalActualConsumption, this must be called exactly once per hour.
   */
  @Override
  public void run() {
    try {
      int baselineConsumption = getBaselineConsumption();
      double goalConsumption = getGoalConsumption(baselineConsumption);
      double warningConsumption = getWarningConsumption(baselineConsumption);

      startWattDepotTiming();
      double actualConsumption = getActualConsumption();
      stopWattDepotTiming();
      
      // Now update the spreadsheet.
      startGDataTiming();
      GDataBatchRequest request = new GDataBatchRequest(gdataService, spreadsheetURL);
      // Update the lastUpdate cell, the actual cell, and the goal cell.
      request.addRequest(this.row, 2, gdataService.makeLastUpdate());
      request.addRequest(this.row, 3, String.format("%3.1f", (actualConsumption / 1000)));
      request.addRequest(this.row, 4, String.format("%3.1f", (goalConsumption / 1000)));
      request.addRequest(this.row, 5, String.format("%3.1f", (warningConsumption / 1000)));
      request.sendRequest();
      updateHistoricalDataSpreadsheet();
      stopGDataTiming();
      printTaskResults(String.valueOf(actualConsumption), String.valueOf(goalConsumption), 
          "actual/goal");
    }
    catch (Exception e) {
      System.out.format("Problem retrieving goal data for source %s%n", sourceName);
    }
  }
  
  /**
   * Update the historical spreadsheet for this source. 
   */
  private void updateHistoricalDataSpreadsheet() {
    try {
      String spreadsheetName = "EnergyLastThirtyDaysBetaEval";
      URL url = gdataService.getSpreadsheetURL(spreadsheetName);
      GDataBatchRequest request = new GDataBatchRequest(gdataService, url);
      request.addRequest(this.row, 1, sourceName);
      request.addRequest(this.row, 2, gdataService.makeLastUpdate());
      int column = 3;
      // Write out the last 30 days of data. 
      XMLGregorianCalendar tstamp = makeDateStamp();
      for (int day = 0; day < 30; day++) {
        XMLGregorianCalendar currDay = Tstamp.incrementDays(tstamp, -day);
        int consumption = actuals.getConsumption(currDay);
        request.addRequest(this.row, column, String.valueOf(consumption));
        column++;
      }
      request.sendRequest();
    }
    catch (Exception e) {
      System.out.println("Error updating BetaEval history spreadsheet");
      e.printStackTrace();
    }
  }
  
  /**
   * Makes today's date with hours, minutes, seconds, and milliseconds set to zero.
   * @return Today's date stamp.
   */
  public XMLGregorianCalendar makeDateStamp() {
    XMLGregorianCalendar tstamp = Tstamp.makeTimestamp();
    tstamp.setHour(0);
    tstamp.setMinute(0);
    tstamp.setSecond(0);
    tstamp.setMillisecond(0);
    return tstamp;
  }
  
  

}
